import { Server as WebSocketServer, Socket } from 'socket.io';
import { defaultSqlDriver } from '../sql/sql';
import user from '../httpServer/servers/user';

/**
 * 服务器器类。
 */
export class WSSServer {
  protected io: WebSocketServer;
  //通过SocketId查找用户id的Map
  protected userIdMap: Map<string, string> = new Map();
  //通过用户id查找Socket连接的Map
  protected socketMap: Map<string, Socket> = new Map();
  /**
   * 创建一个新的调度器实例。
   *
   * @param {WebSocketServer} io WebSocket服务实例
   */
  constructor(io: WebSocketServer) {
    this.io = io;
  }

  /**
   * 初始化调度器，监听WebSocket连接及相关事件。
   */
  init() {
    this.io.on('connection', (socket) => {
      // 处理客户端连接
      socket.on('client-connection', (userId: string, friendIds: string[]) => {
        this.handleClientConnection(socket, userId, friendIds);
      });

      // 添加对disconnection的监听
      socket.on('disconnect', async (reason) => {
        this.handleDisconnect(socket, reason);
      });

      // 处理申请添加好友
      socket.on('apply-add-friend', (applierId: string, targetId: string) => {
        this.handleApplyAddFriend(socket, applierId, targetId);
      });

      // 处理同意添加好友
      socket.on('agree-add-friend', (senderId: string) => {
        this.handleAgreeAddFriend(socket, senderId);
      });

      // 处理拒绝添加好友
      socket.on('reject-add-friend', (senderId: string) => {
        this.handleRejectAddFriend(socket, senderId);
      });

      // 处理私聊申请
      socket.on(
        'apply-private-chat',
        (
          senderInfo: {
            senderId: string;
            dhPublicKey: string;
            rsaPublicKey: string;
            sign: string;
          },
          receiverId: string
        ) => {
          this.handleApplyPrivateChat(socket, senderInfo, receiverId);
        }
      );

      // 转发私聊消息
      socket.on(
        'private-chat-message',
        (
          friendId: string,
          msg: {
            iv: string;
            encrypted: string;
          },
          time: number,
          friendName: string
        ) => {
          this.handlePrivateChatMessage(
            socket,
            friendId,
            msg,
            time,
            friendName
          );
        }
      );

      // 处理删除好友
      socket.on('delete-friend', (userId, friendId, friendName) => {
        this.handleDeleteFriend(socket, userId, friendName, friendId);
      });

      // 获取好友在线状态
      socket.on('client-online-status', (ids: string[]) => {
        this.handleClientOnlineStatus(socket, ids);
      });
    });
  }

  handleClientOnlineStatus(socket: Socket, ids: string[]) {
    socket.emit(
      'client-online-status',
      ids.map((id) => {
        return {
          id: id,
          isLine: this.socketMap.has(id)
        };
      })
    );
  }

  async handleDisconnect(socket: Socket, reason: string) {
    //查找该用户的所有好友
    const userId = this.userIdMap.get(socket.id) as string;
    this.socketMap.delete(userId);
    this.userIdMap.delete(socket.id);
    if (!userId) {
      console.log('无法找到该连接的id');
      return;
    }
    const result = (await user.getContactList(1, 100, userId)) as {
      code: number;
      msg: string;
      data: Array<{
        friendId: string;
        friendName: string;
        createTime: Date;
      }>;
    };

    for (const item of result.data) {
      const friendSocket = this.socketMap.get(item.friendId);
      if (friendSocket) {
        friendSocket.emit('friend-online', userId, false);
      }
    }
  }

  /**
   * 删除好友
   * @param socket 发送方Socket
   * @param userId 发送方ID
   * @param friendId 好友ID
   */
  async handleDeleteFriend(
    socket: Socket,
    senderId: string,
    senderName: string,
    friendId: string
  ) {
    /**
     * 表示对方是否是我的好友
     */
    const isFriendOfMine = await this.searchFriend(senderId, friendId);

    let returnResult = {
      code: 1, //0表示成功，1表示删除失败，2表示对方接收的消息
      msg: '对方不是你的好友',
      friendId: friendId,
      senderId: senderId
    };

    if (isFriendOfMine === false) {
      returnResult.code = 1;
      returnResult.msg = '对方不是你的好友';
      socket.emit('delete-friend', returnResult);
      console.log('不是好友，无法删除');
      return;
    }

    const deleteSql = `
        delete from friends_info where (user_id = ? and friend_id = ?) or (user_id = ? and friend_id = ?)
      `;

    try {
      //删除好友
      await defaultSqlDriver.execute(deleteSql, [
        senderId,
        friendId,
        friendId,
        senderId
      ]);
    } catch (error) {
      returnResult.code = 1;
      returnResult.msg = '服务器异常：好友删除失败';
      socket.emit('delete-friend', returnResult);
      console.log(error);
    }

    const insertSystemMessageSql = `
        insert into system_message (sender_id, receiver_id, status, type) values (?, ?, ?, ?);
      `;
    try {
      //记录我的系统消息
      await defaultSqlDriver.execute(insertSystemMessageSql, [
        senderId,
        friendId,
        isFriendOfMine ? 0 : 1,
        1
      ]);
    } catch (error) {}

    returnResult.code = 0;
    returnResult.msg = '删除成功';
    socket.emit('delete-friend', returnResult);

    //查找对方是否在线
    if (this.socketMap.has(friendId)) {
      returnResult.code = 2;
      returnResult.msg = `${senderName} 删除了你`;
      //如果在线则发送删除好友消息
      this.socketMap.get(friendId)!.emit('delete-friend', returnResult);
    }
  }

  /**
   * 转发处理私聊消息
   * @param socket 发送方的Socket
   * @param friendId 好友id
   * @param msg 消息密文
   */
  async handlePrivateChatMessage(
    socket: Socket,
    friendId: string,
    msg: {
      iv: string;
      encrypted: string;
    },
    time: number,
    senderName: string
  ) {
    //获取好友的Socket
    const targetSocket = this.socketMap.get(friendId);
    //获取发送方的id
    const senderId = this.userIdMap.get(socket.id) as string;

    if (!targetSocket || !senderId) {
      socket.emit('private-chat-message-ack', {
        status: false,
        msg: '用户不在线',
        senderId: senderId,
        receiverId: friendId,
        time: time
      });
      return;
    }
    socket.emit('private-chat-message-ack', {
      status: true,
      msg: '消息发送成功',
      senderId: senderId,
      receiverId: friendId,
      time: time
    });
    //转发消息到好友的Socket
    targetSocket.emit('private-chat-message', {
      senderId,
      receiverId: friendId,
      msg,
      time,
      senderName
    });
  }

  /**
   * 处理私聊申请
   * @param socket
   * @param senderId
   * @param targetId
   */
  handleApplyPrivateChat(
    socket: Socket,
    senderInfo: {
      senderId: string;
      dhPublicKey: string;
      rsaPublicKey: string;
      sign: string;
    },
    receiverId: string
  ) {
    // 获取目标用户的Socket
    const targetSocket = this.socketMap.get(receiverId);
    if (!targetSocket) {
      socket.emit('server-response', {
        method: 'user-online',
        code: 0,
        data: '目标用户不在线'
      });
      console.error(`目标用户 ${receiverId} 当前不在线，无法申请私聊。`);
    } else {
      // 把申请人的公钥发送给目标用户，目标用户则发送自己的公钥给服务器
      targetSocket.emit('apply-private-chat', {
        receiverId,
        senderInfo: {
          senderId: senderInfo.senderId,
          dhPublicKey: senderInfo.dhPublicKey,
          rsaPublicKey: senderInfo.rsaPublicKey,
          sign: senderInfo.sign
        }
      });

      //接收目标用户的公钥信息,并转发给申请人
      targetSocket.once(
        'get-public-key',
        (data: {
          userId: string;
          dhPublicKey: string;
          rsaPublicKey: string;
          sign: string;
        }) => {
          socket.emit('target-negotiation-info', data);
        }
      );

      //监听密钥协商结果
      targetSocket.on(
        'key-agreement-result',
        (data: { senderId: string; receiverId: string; status: boolean }) => {
          // 向申请人返回密钥协商结果
          socket.emit('key-agreement-result', {
            senderId: data.senderId,
            receiverId: data.receiverId,
            status: data.status
          });
        }
      );

      socket.on(
        'key-agreement-result',
        (data: { senderId: string; receiverId: string; status: boolean }) => {
          // 向目标用户返回密钥协商结果
          targetSocket.emit('key-agreement-result', {
            senderId: data.senderId,
            receiverId: data.receiverId,
            status: data.status
          });
        }
      );
    }
  }

  /**
   * 处理客户端连接事件。
   *
   * @param {Socket} socket 客户端的Socket对象
   * @param {string} userId 用户的ID
   */
  async handleClientConnection(
    socket: Socket,
    userId: string,
    friendIds: string[]
  ): Promise<void> {
    this.socketMap.set(userId, socket);
    this.userIdMap.set(socket.id, userId);
    socket.join(userId);

    for (const item of friendIds) {
      const friendSocket = this.socketMap.get(item);
      if (friendSocket) {
        friendSocket.emit('friend-online', userId, true);
      }
    }

    this.io.to(userId).emit('client-connection', {
      userId,
      msg: '客户端连接完成'
    });
  }

  /**
   * 处理添加好友请求。
   *
   * @param {Socket} socket 发起请求的客户端的Socket对象
   * @param {string} senderId 申请人ID
   * @param {string} targetId 被申请人ID
   */
  async handleApplyAddFriend(
    socket: Socket,
    senderId: string,
    targetId: string
  ): Promise<void> {
    try {
      // 查询是否有该用户
      const querySql = `select id as userId, \`name\` from user_info where id = ?`;
      const [result, _] = (await defaultSqlDriver.query(querySql, [
        senderId
      ])) as any[];

      //查询该好友是否已经被申请
      const queryAppliedSql = `select id from system_message where type = 0 and sender_id = ? and receiver_id = ?`;
      const [applied] = await defaultSqlDriver.query(queryAppliedSql, [
        senderId,
        targetId
      ]);
      const appliedResult: Array<any> = applied as any;

      console.log('查询结果：', appliedResult);
      if (appliedResult.length > 0) {
        socket.emit('apply-add-friend-ack', {
          status: false,
          msg: '该好友已经申请过了'
        });
        return;
      }
      const insertSql = `insert into system_message (sender_id, receiver_id, status, type) values (?, ?, ?, ?)`;
      const status = 0;
      const type = 0;
      await defaultSqlDriver.execute(insertSql, [
        senderId,
        targetId,
        status,
        type
      ]);
      
      socket.emit('apply-add-friend-ack', {
        status: true,
        msg: '申请成功'
      });

      this.socketMap.get(targetId)?.emit('apply-add-friend', {
        status: true,
        msg: '有好友申请',
        data: result[0]
      });
    } catch (error) {
      console.log(error);
    }
  }

  //处理同意
  async handleAgreeAddFriend(socket: Socket, senderId: string): Promise<void> {
    try {
      const userId = this.userIdMap.get(socket.id);
      // 更新消息状态
      const updateSql = `update system_message set status = 1 where sender_id = ? and receiver_id = ?`;
      // 插入好友数据
      const insertSql = `insert into friends_info (user_id, friend_id) values (?, ?)`;
      await defaultSqlDriver.execute(updateSql, [senderId, userId]);
      await defaultSqlDriver.execute(insertSql, [senderId, userId]);
      //尝试跟向对方发消息，说明被申请人已经同意
      this.socketMap.get(senderId)?.emit('agree-add-friend', {
        receiverId: userId,
        msg: '同意申请',
        code: 1
      });
    } catch (error) {
      console.error(error);
      socket.emit('agree-add-friend', false);
      this.socketMap.get(senderId)?.emit('agree-add-friend', {
        receiverId: '',
        msg: '服务器异常',
        code: -1
      });
    }
  }

  //处理拒绝
  async handleRejectAddFriend(socket: Socket, senderId: string): Promise<void> {
    try {
      const userId = this.userIdMap.get(socket.id);
      const updateSql = `update system_message set status = 2 where sender_id = ? and receiver_id = ?`;
      await defaultSqlDriver.execute(updateSql, [senderId, userId]);
      socket.emit('reject-add-friend', true);
      this.io.to(senderId).emit('reject-add-friend', {
        receiverId: this.userIdMap.get(socket.id),
        msg: '拒绝申请',
        code: 1
      });
    } catch (error) {
      console.error(error);
      socket.emit('reject-add-friend', false);
      this.io.to(senderId).emit('reject-add-friend', {
        receiverId: '',
        msg: '服务器异常',
        code: -1
      });
    }
  }

  /**
   * 搜索是否存在该好友
   */
  async searchFriend(userId: string, friendId: string): Promise<boolean> {
    //查询有没有该好友
    const querySql = `
      select user_id,friend_id from friends_info where user_id = ? and friend_id = ?
      UNION
      select user_id,friend_id from friends_info where friend_id = ? and user_id = ?
    `;
    const [queryResultTemp] = await defaultSqlDriver.query(querySql, [
      userId,
      friendId,
      userId,
      friendId
    ]);

    const queryResult = queryResultTemp as any[];

    return (
      queryResult[0].user_id === userId || queryResult[0].friend_id === userId
    );
  }
}
